﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
using System.Diagnostics;
using System.Collections;
using System.Runtime.InteropServices;

using SUnit.Core;
using SUnit.Core.SchemaClasses;
using System.Reflection;

namespace SUnit.ConsoleUI
{
    class Program
    {
        static StringBuilder consoleBuilder = new StringBuilder();
        static SummaryGenerator summary = new SummaryGenerator();

        static string dllToTest = null;
        static string xmlToTest = null;
        static string exportFile = null;
        static bool measureTime = false;
        static bool checkMemory = false;
        static bool showStdout = false;
        static bool showStderr = false;

        static void Main(string[] args)
        {
            _parseConsoleArgs(args);

            try
            {
                TestInfo test = null;
                sUnit sUnitTests = null;

                //Cargo un caso de prueba estático
                if (Path.GetExtension(xmlToTest).ToLower() == ".xml")
                {
                    XmlDocument xmlTests = new XmlDocument();
                    xmlTests.Load(xmlToTest);

                    if (XMLHelper.ValidateTestFile(xmlTests))
                    {
                        //Cargo los tests si el xml indicado es válido
                        sUnitTests = XMLHelper.DeserializeTestFile(xmlTests);
                        //Creo la jerarquía de TestInfos
                        test = new TestInfo(Path.GetFileNameWithoutExtension(xmlToTest), sUnitTests.testCases);
                    }
                    else
                    {
                        //Termino el programa si hay un error de validación en el XML
                        Console.WriteLine(string.Format("> Error de validación en el XML:\n{0}", XMLHelper.ValidationError));
                        Environment.Exit(0);
                    }
                }
                //Cargo un caso de prueba dinámico
                else
                {
                    Hashtable tests = DynamicTestCaseCompiler.GetTestCases(xmlToTest, Environment.CurrentDirectory);
                    //Creo la jerarquía de TestInfos
                    test = new TestInfo(Path.GetFileNameWithoutExtension(xmlToTest), tests);
                }

				Console.WriteLine("Presionar una tecla para correr los casos de prueba...");
				Console.ReadKey();

                TestRunner runner = new TestRunner(test);
                runner.TestCompleted += new TestCompletedEventHandler(runner_TestCompleted);

                sUnitDataType[] dataTypes = null;

                if (sUnitTests != null)
                    dataTypes = sUnitTests.dataTypes;

#if MONO
                Tester tester = new Tester(new CompilationUnit(dllToTest, dllToTest.EndsWith(".so")), dataTypes);
#else
                Tester tester = new Tester(new CompilationUnit(dllToTest, dllToTest.EndsWith(".dll")), dataTypes);
#endif

                tester.MeasureTime = measureTime;
                tester.CheckMemoryLeaks = checkMemory;

                DynamicTest.tester = tester;
                runner.Run(tester);

                //Borro la consola por cualquier mensaje que pueda provenir del código de usuario
                Console.Clear();
            }
            catch (Exception ex)
            {
                Console.WriteLine("> Error: {0}", ex.Message);
            }

            //Muestro los resultados
            Console.WriteLine(consoleBuilder);

            //Si corresponde, exporto los resultados
            if (!string.IsNullOrEmpty(exportFile))
            {
                XmlDocument doc = summary.ExportResults();
                doc.Save(exportFile);
            }                

            _exit();
        }

		//Acá se puede especificar cómo se muestra el resultado
		static void runner_TestCompleted(object sender, TestCompletedEventArgs e)
		{
			//Borro la consola por cualquier mensaje que pueda provenir del código de usuario
			Console.Clear();
			TestInfo testInfo = sender as TestInfo;
			
			consoleBuilder.AppendFormat("> Función: {0}\n\n", testInfo.TestCaseGroup.functionName);
			consoleBuilder.AppendLine(e.Result.GetResultInfo());
			
			if (showStdout)
			{
				consoleBuilder.AppendLine("StdOut:");
				consoleBuilder.AppendLine("\t" + e.Result.StdOut.Replace("\n", "\n\t"));
			}

			if (showStderr)
			{
				consoleBuilder.AppendLine("StdErr:");
				consoleBuilder.AppendLine("\t" + e.Result.StdErr.Replace("\n", "\n\t"));
			}

			consoleBuilder.AppendLine("-------------------------------");
			summary.AddResult(testInfo, e.Result);
		}

        static void _parseConsoleArgs(string[] args)
        {
			_showWelcomeMessage();

            for (int i = 0; i < args.Length; i++)
            {
                switch (args[i])
                {
                    case "-p":
                        dllToTest = args[++i];
                        break;
                    case "-t":
                        xmlToTest = args[++i];
                        break;
                    case "-export":
                        exportFile = args[++i];
                        break;
                    case "-time":
                        measureTime = true;
                        break;
                    case "-mem":
                        checkMemory = true;
                        break;
                    case "-stdout":
                        showStdout = true;
                        break;
                    case "-stderr":
                        showStderr = true;
                        break;
                    default:
						_showHelpMessage();
                        Console.WriteLine("> Comando inválido: {0}", args[i]);
                        _exit();
                        break;
                }
            }

            //Sólo el programa y el caso de test son obligatorios
			if (string.IsNullOrEmpty(dllToTest))
			{
				_showHelpMessage();
				Console.WriteLine("> No se especificó el programa a testear");
				_exit();
			}
			else
			{
				Console.WriteLine("> Programa a testear:");
				Console.WriteLine(string.Format("\t{0}", dllToTest));
				Console.WriteLine();
			}

			if (string.IsNullOrEmpty(xmlToTest))
			{
				_showHelpMessage();
				Console.WriteLine("> No se especificó el caso de prueba");
				_exit();
			}
			else
			{
				Console.WriteLine("> Caso de prueba:");
				Console.WriteLine(string.Format("\t{0}", xmlToTest));
				Console.WriteLine();
			}
        }

		static void _showWelcomeMessage()
		{
			Assembly assembly = Assembly.GetExecutingAssembly();

			string version = assembly.GetName().Version.ToString(3);
			string title = (assembly.GetCustomAttributes(typeof(AssemblyTitleAttribute), false)[0] as AssemblyTitleAttribute).Title;
			string company = (assembly.GetCustomAttributes(typeof(AssemblyCompanyAttribute), false)[0] as AssemblyCompanyAttribute).Company;
			string copyright = (assembly.GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false)[0] as AssemblyCopyrightAttribute).Copyright;

			Console.WriteLine(string.Format("* {0} * - v{1}", title, version));
			Console.WriteLine();
			Console.WriteLine(string.Format("{0} - {1}", company, copyright));
			Console.WriteLine();
		}

        static void _showHelpMessage()
        {
			Console.WriteLine("Uso:\t-p nombreDePrograma -t nombreDeCasoDeTest [-time] [-mem] [-stdout] [-stderr] [-export nombreArchivoDeSalida]");
			Console.WriteLine();
        }

        static void _exit()
        {
			Console.ReadKey();
            Environment.Exit(0);
        }

    }
}
